package com.projectCommon.util.springUtil;

import com.projectCommon.util.springUtil.annotation.MethodEventDescription;
import com.projectCommon.bean.cache.EventFlag;
import com.projectCommon.excetion.AspException;
import com.projectCommon.excetion.MatcherException;
import com.projectCommon.util.threadSave.ThreadLogHold;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 吴宇~
 * @motto 我亦无他，唯手熟尔。
 * @edition jdk1.8
 * @reflection 0-1 喧闹任其喧闹，自由我自为之，我自风情万种，与世无争。
 * @date 2024/04/05/10:04
 * @target 心若有所向往，何惧道阻且长
 */
@Aspect
@Slf4j
public class SpringAspectConfigWyx {
    @Pointcut("@annotation(com.projectCommon.util.springUtil.annotation.MethodEventDescription)")
    public void methodEventDesc() {
    }

    /**
     * 这儿地区没有一个 好的架构，记录日志信息。 还需要用户信息，说不来，到时候再看吧，想要一个记录什么信息的日志。
     * 日志格式定义一下再开始写。给一个跟踪标识。就和公司的一样  TODO：。。。
     *
     * @param joinPoint
     */
    @Around("methodEventDesc()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) {
        String methodArgs = getMethodArgs(joinPoint);
        String eventLog = getEventLog(joinPoint);
        //子方法耗时
        MethodEventDescription methodDescription = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(MethodEventDescription.class);
        String logLevel = methodDescription.logLevel();
        ThreadLogHold.eventStartCountAdd();
        printStartLog(eventLog, methodArgs, logLevel);
        long startTime = System.currentTimeMillis();
        boolean b = ThreadLogHold.addEventFlag(new EventFlag(ThreadLogHold.getCounter(), 0, eventLog));
        if (!b) {
            throw new AspException("添加异常");
        }

        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        String cost = (endTime - startTime) + " ms";
        ThreadLogHold.addEventFlag(new EventFlag(ThreadLogHold.getCounter(), 1, eventLog, cost));

        String sonEvent = "";
        if (methodDescription.isShowAllEventCost()) {
            sonEvent = ", son event [" + getSonEventCostStr() + "]";
        }
        printEndLog(eventLog, cost, sonEvent, logLevel);
        ThreadLogHold.eventEndCountAdd();
        return result;
    }

    /**
     * 得到子方法耗时字符串
     *
     * @return 返回子方法耗时字符串
     */
    private String getSonEventCostStr() {
        StringBuilder sb = new StringBuilder();
        List<EventFlag> eventFlags = ThreadLogHold.getEventFlags();
        if (eventFlags.get(eventFlags.size() - 1).getFlag() == 0) {
            throw new AspException("最后一个咋就成了开始了呢？？");
        }
        int startCount = 0;
        int endCount = 0;
        for (int i = eventFlags.size() - 1; i >= 0; i--) {
            EventFlag eventFlag1 = eventFlags.get(i);
            if (eventFlag1.getFlag() == 0) {
                startCount++;
            } else if (eventFlag1.getFlag() == 1) {
                endCount++;
            } else {
                throw new AspException("标识异常");
            }
            if (endCount == startCount) {
                //如果相等，那么就是找到了，开始的位置。
                int eventCount = eventFlag1.getEventCont();
                int SstartCount = 0;
                int EendCount = 0;
                if (eventFlags.size() - eventCount == 1) {
                    return null;
                }
                for (int i1 = eventCount; i1 < eventFlags.size() - 1; i1++) {
                    EventFlag eventFlag2 = eventFlags.get(i1);
                    if (eventFlag2.getFlag() == 1) {
                        EendCount++;
                        if (SstartCount == EendCount) {
                            sb.append(eventFlag2.getEventLog()).append(" ").append("cost").append(" ").append(eventFlag2.getCost()).append(";").append(" ");
                        }
                    } else if (eventFlag2.getFlag() == 0) {
                        SstartCount++;
                    }
                }
                break;
            }
        }
        if (sb.length() > 0) {
            sb.setLength(sb.length() - 2); // 删除最后的空格和分号
        }
        return sb.toString();
    }

    /**
     * 得到事件日志
     *
     * @param joinPoint
     * @return
     */
    private String getEventLog(ProceedingJoinPoint joinPoint) {
        String eventLog = "";
        MethodEventDescription methodDescription = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(MethodEventDescription.class);
        String event = methodDescription.event();
        if (!methodDescription.isFillMethodArg()) {
            return event;
        } else {
            Object[] args = joinPoint.getArgs();
            eventLog = methodDescription.isFillMethodArg() ? replacePlaceholder(event, args) : event;
        }
        return eventLog;
    }

    /**
     * @param event  事件
     * @param params 数组参数
     * @return 替换其中的占位符
     */
    public String replacePlaceholder(String event, Object[] params) {
        Pattern pattern = Pattern.compile("\\{\\{#(\\d+)}}");
        Matcher matcher = pattern.matcher(event);
        boolean isMatch = false;
        while (matcher.find()) {
            isMatch = true;
            // 获取整个匹配的字符串
            String match = matcher.group(0);
            // 获取捕获组中的数字
            int number = Integer.parseInt(matcher.group(1));
            if (number > params.length) {
                throw new MatcherException("方法参数数量小于需要获取的参数下标");
            }
            event = event.replace(match, params[number - 1].toString());
        }
        if (!isMatch) {
            //说明一个都没匹配到，就说明是代码的问题。
            throw new MatcherException("一个都没匹配到，那么说明写错了");
        }
        return event;
    }

    /**
     * 得到方法参数字符串
     *
     * @return
     */
    public String getMethodArgs(ProceedingJoinPoint joinPoint) {
        String methodArgs = "";
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        MethodEventDescription methodDescription = signature.getMethod().getAnnotation(MethodEventDescription.class);
        Object[] args = joinPoint.getArgs();
        if (methodDescription.isShowAllArg()) {
            StringBuilder methodParams = new StringBuilder();
            for (int i = 0; i < signature.getParameterNames().length; i++) {
                String argValue;
                if (args[i] instanceof Integer || args[i] instanceof Long || args[i] instanceof Float ||
                        args[i] instanceof Double || args[i] instanceof Byte || args[i] instanceof Short ||
                        args[i] instanceof Character || args[i] instanceof Boolean || args[i] instanceof String) {
                    argValue = args[i].toString();
                } else {
                    argValue = args[i].getClass().toString();
                }
                methodParams.append(signature.getParameterNames()[i]).append("=").append(argValue).append(";");
            }
            if (signature.getParameterNames().length > 0) {
                methodParams.deleteCharAt(methodParams.length() - 1);
            }
            methodArgs = ", method params : [" + methodParams + "]";
        }
        return methodArgs;
    }

    /**
     * 打印日志
     *
     * @param eventLog 日志事件
     * @param cost     耗时
     * @param sonEvent 子事件
     * @param logLevel 日志级别
     */
    public void printEndLog(String eventLog, String cost, String sonEvent, String logLevel) {
        switch (logLevel) {
            case LogConstant.LEVER_INFO:
                log.info(LogConstant.LOG_EVENT_END, eventLog, cost, sonEvent);
                break;
            case LogConstant.LEVER_DEBUG:
                log.debug(LogConstant.LOG_EVENT_END, eventLog, cost, sonEvent);
                break;
            case LogConstant.LEVER_WARN:
                log.warn(LogConstant.LOG_EVENT_END, eventLog, cost, sonEvent);
                break;
            case LogConstant.LEVER_ERROR:
                log.error(LogConstant.LOG_EVENT_END, eventLog, cost, sonEvent);
                break;
            default:
                log.info(LogConstant.LOG_EVENT_END, eventLog, cost, sonEvent);
                break;
        }
    }
    /**
     * 打印开始日志
     * @param eventLog 日志事件
     * @param methodArgs 法参数
     * @param logLevel 日志级别
     */
    private void printStartLog(String eventLog, String methodArgs, String logLevel) {
        switch (logLevel) {
            case LogConstant.LEVER_INFO:
                log.info(LogConstant.LOG_EVENT_START, eventLog, methodArgs);
                break;
            case LogConstant.LEVER_DEBUG:
                log.debug(LogConstant.LOG_EVENT_START, eventLog, methodArgs);
                break;
            case LogConstant.LEVER_WARN:
                log.warn(LogConstant.LOG_EVENT_START, eventLog, methodArgs);
                break;
            case LogConstant.LEVER_ERROR:
                log.error(LogConstant.LOG_EVENT_START, eventLog, methodArgs);
                break;
            default:
                log.info(LogConstant.LOG_EVENT_START, eventLog, methodArgs);
                break;
        }
    }
}